package org.example.springboot.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.springboot.common.config.XxlJobConfig;
import org.example.springboot.common.page.PageResult;
import org.example.springboot.common.service.impl.BaseServiceImpl;
import org.example.springboot.common.utils.DateUtils;
import org.example.springboot.common.utils.XxlJobUtil;
import org.example.springboot.convert.IrrigationUnitConvert;
import org.example.springboot.dao.FmcBeaconDao;
import org.example.springboot.dao.IrrigationUnitDao;
import org.example.springboot.entity.*;
import org.example.springboot.query.IrrigationUnitQuery;
import org.example.springboot.service.*;
import org.example.springboot.vo.IrrigationUnitVO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Fz
 * @since 1.0.0 2024-07-31
 */
@Slf4j
@Service
@AllArgsConstructor
public class IrrigationUnitServiceImpl extends BaseServiceImpl<IrrigationUnitDao, IrrigationUnit> implements IrrigationUnitService {

    private final RationService rationService;

    private final CycleService cycleService;

    private final MoistureControlService moistureControlService;

    private final ETService E_TService;

    private final ColDeviceService colDeviceService;

    private final ColDeviceUserService colDeviceUserService;

    private final ColDeviceDataSetService colDeviceDataSetService;

    private final ColDeviceAlarmSetService colDeviceAlarmSetService;

    private final IrrigationLogService irrigationLogService;

    private final FmcBeaconDao fmcBeaconDao;

    private final FmcMetricsService fmcMetricsService;

    @Resource
    private final XxlJobConfig xxlJobConfig;

   /* @Override
    public PageResult<IrrigationUnitVO> page(IrrigationUnitQuery query) {
        IPage<IrrigationUnit> page = this.page(query, new QueryWrapper<IrrigationUnit>().orderByDesc("create_date"));
        List<IrrigationUnitVO> records = new ArrayList<>();
        for (IrrigationUnit irrigationUnit : page.getRecords()) {
            records.add(getIrrigationUnitById(irrigationUnit.getId()));
        }
        return new PageResult<>(records, page.getTotal());
    }*/

    public PageResult<IrrigationUnitVO> page(IrrigationUnitQuery query) {
        // 执行分页查询
        QueryWrapper<IrrigationUnit> irrigationUnitQueryWrapper = new QueryWrapper<>();
        if(!Objects.isNull(query.getType())){
            irrigationUnitQueryWrapper.eq("type", query.getType());
        }
        irrigationUnitQueryWrapper.orderByDesc("create_date");
        IPage<IrrigationUnit> page = this.page(query, irrigationUnitQueryWrapper);

        // 获取所有 IrrigationUnit 的 UUID
        List<String> uuids = page.getRecords().stream()
                .map(IrrigationUnit::getUuid)
                .collect(Collectors.toList());

        // 批量查询所有相关数据
        if(uuids.isEmpty()){
            return new PageResult<>(Collections.emptyList(), 0L);
        }
        List<Ration> rations = rationService.list(new LambdaQueryWrapper<Ration>().in(Ration::getIrrigationUnitUuid, uuids));
        List<Cycle> cycles = cycleService.list(new LambdaQueryWrapper<Cycle>().in(Cycle::getIrrigationUnitUuid, uuids));
        List<MoistureControl> moistureControls = moistureControlService.list(new LambdaQueryWrapper<MoistureControl>().in(MoistureControl::getIrrigationUnitUuid, uuids));
        List<ET> ets = E_TService.list(new LambdaQueryWrapper<ET>().in(ET::getIrrigationUnitUuid, uuids));

        // 批量查询 FmcMetrics 数据
        List<FmcMetrics> fmcMetricsList = fmcMetricsService.list(
                new QueryWrapper<FmcMetrics>().in("did", page.getRecords().stream().map(IrrigationUnit::getControlDevice).collect(Collectors.toList()))
                        .orderByDesc("id")
                        .last("LIMIT " + page.getRecords().size())
        );

        // 构建 IrrigationUnitVO 列表
        List<IrrigationUnitVO> records = new ArrayList<>();
        for (IrrigationUnit irrigationUnit : page.getRecords()) {
            IrrigationUnitVO vo = getIrrigationUnitVO(irrigationUnit, rations, cycles, moistureControls, ets, fmcMetricsList);
            records.add(vo);
        }

        return new PageResult<>(records, page.getTotal());
    }

    private IrrigationUnitVO getIrrigationUnitVO(IrrigationUnit irrigationUnit, List<Ration> rations, List<Cycle> cycles,
                                                 List<MoistureControl> moistureControls, List<ET> ets, List<FmcMetrics> fmcMetricsList) {
        IrrigationUnitVO vo = IrrigationUnitConvert.INSTANCE.convert(irrigationUnit);

        // 获取相关数据
        Ration ration = rations.stream()
                .filter(r -> r.getIrrigationUnitUuid().equals(vo.getUuid()))
                .findFirst()
                .orElse(null);
        Cycle cycle = cycles.stream()
                .filter(c -> c.getIrrigationUnitUuid().equals(vo.getUuid()))
                .findFirst()
                .orElse(null);
        MoistureControl moistureControl = moistureControls.stream()
                .filter(m -> m.getIrrigationUnitUuid().equals(vo.getUuid()))
                .findFirst()
                .orElse(null);
        ET et = ets.stream()
                .filter(e -> e.getIrrigationUnitUuid().equals(vo.getUuid()))
                .findFirst()
                .orElse(null);

        // 设置相关信息
        vo.setRationTypeMsg(ration != null ? (JSONObject) JSONObject.toJSON(ration) : null);
        vo.setCycleTypeMsg(cycle != null ? (JSONObject) JSONObject.toJSON(cycle) : null);
        vo.setMoistureControlTypeMsg(moistureControl != null ? (JSONObject) JSONObject.toJSON(moistureControl) : null);
        vo.setETTypeMsg(et != null ? (JSONObject) JSONObject.toJSON(et) : null);

        // 获取 FmcMetrics
        FmcMetrics fmcMetrics = fmcMetricsList.stream()
                .filter(f -> f.getDid().equals(vo.getControlDevice()))
                .findFirst()
                .orElse(null);
        if (fmcMetrics != null) {
            vo.setFlowRemaining(fmcMetrics.getFlowRem());
            vo.setEvapotranspiration(fmcMetrics.getEt());
        }

        return vo;
    }

    /**
     * 保存灌溉单元信息
     *
     * @param vo 灌溉单元视图对象，必传
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doSave(IrrigationUnitVO vo) {
        if (ObjectUtil.isNull(vo.getUuid())) {
            vo.setUuid(UUID.randomUUID().toString());
        }
        IrrigationUnit entity = IrrigationUnitConvert.INSTANCE.convert(vo);

        Optional.ofNullable(vo.getRationTypeMsg()).ifPresent(m -> rationService.doSaveOrUpdate(vo.getRationTypeMsg(), vo.getUuid()));
        Optional.ofNullable(vo.getCycleTypeMsg()).ifPresent(m -> cycleService.doSaveOrUpdate(vo.getCycleTypeMsg(), vo.getUuid()));
        Optional.ofNullable(vo.getMoistureControlTypeMsg()).ifPresent(m -> moistureControlService.doSaveOrUpdate(vo.getMoistureControlTypeMsg(), vo.getUuid()));
        Optional.ofNullable(vo.getETTypeMsg()).ifPresent(m -> E_TService.doSaveOrUpdate(vo.getETTypeMsg(), vo.getUuid()));

        this.saveOrUpdate(entity);
    }

    /**
     * 根据用户ID和设备类型查询设备列表
     *
     * @param userId  用户ID
     * @param devType 设备类型
     * @return 返回符合条件的设备列表
     */
    @Override
    public List<ColDevice> queryDevicesByUserId(Long userId, Integer devType) {
        List<ColDeviceUser> colDeviceUsers = colDeviceUserService.list(new QueryWrapper<ColDeviceUser>()
                .eq("user_id", userId)
                .eq("del_flag", 0));
        List<Long> deviceIds = colDeviceUsers.stream().map(ColDeviceUser::getDeviceId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(deviceIds)) {
            return new ArrayList<>();
        }
        return colDeviceService.list(new QueryWrapper<ColDevice>().in("id", deviceIds).eq("dev_type", devType));
    }

    /**
     * 根据设备编号查询设备属性值列表
     *
     * @param deviceId 设备id
     * @return 设备属性值列表
     */
    @Override
    public List<ColDeviceDataSet> queryDeviceValueByNo(Long deviceId) {
        return colDeviceDataSetService.list(new LambdaQueryWrapper<ColDeviceDataSet>().eq(ColDeviceDataSet::getDeviceId, deviceId));
    }

    /**
     * 检查设备
     * 此方法用于检查设备状态，并根据报警标准信息、最新心跳信息执行一系列的检查操作。
     * 首先，通过查询数据库获取报警标准信息列表和最新心跳信息列表。
     * 然后，如果两个列表都不为空，则对最新心跳信息列表中的每个心跳信息进行遍历，并执行以下操作：
     * <ul>
     *     <li>调用 {@link #checkVoltageAlarm(ColDeviceAlarmSet, FmcBeacon)} 方法检查电压报警</li>
     *     <li>调用 {@link #checkSignalAlarm(ColDeviceAlarmSet, FmcBeacon)} 方法检查信号报警</li>
     *     <li>调用 {@link #checkOfflineAlarm(FmcBeacon)} 方法检查离线报警</li>
     * </ul>
     */
    @Override
    public void checkDevice() {
        // 报警标准信息
        List<ColDeviceAlarmSet> deviceAlarmSetList = colDeviceAlarmSetService.list(new QueryWrapper<ColDeviceAlarmSet>().orderByDesc("create_date").last("limit 1"));
        // 最新心跳信息
        List<FmcBeacon> fmcBeaconList = fmcBeaconDao.selectLatestBeaconsByDid();

        if (!CollectionUtils.isEmpty(deviceAlarmSetList) && !CollectionUtils.isEmpty(fmcBeaconList)) {
            ColDeviceAlarmSet colDeviceAlarmSet = deviceAlarmSetList.get(0);
            for (FmcBeacon fmcBeacon : fmcBeaconList) {
                checkVoltageAlarm(colDeviceAlarmSet, fmcBeacon);
                checkSignalAlarm(colDeviceAlarmSet, fmcBeacon);
                checkOfflineAlarm(fmcBeacon);
            }
        }
    }

    private void checkVoltageAlarm(ColDeviceAlarmSet colDeviceAlarmSet, FmcBeacon fmcBeacon) {
        if (colDeviceAlarmSet.getVolMin() != null && fmcBeacon.getBatt() != null &&
                colDeviceAlarmSet.getVolMin().compareTo(fmcBeacon.getBatt()) <= 0) {
            saveIrrigationLog(fmcBeacon, "电压低");
        }
    }

    private void checkSignalAlarm(ColDeviceAlarmSet colDeviceAlarmSet, FmcBeacon fmcBeacon) {
        if (colDeviceAlarmSet.getSigMin() != null && fmcBeacon.getSignal() != null &&
                colDeviceAlarmSet.getSigMin().compareTo(fmcBeacon.getSignal()) <= 0) {
            saveIrrigationLog(fmcBeacon, "信号弱");
        }
    }

    private void checkOfflineAlarm(FmcBeacon fmcBeacon) {
        long difference = DateUtils.timeDifferenceInMinutes(new Date(), fmcBeacon.getCtime());
        if (difference > 1) {
            saveIrrigationLog(fmcBeacon, "心跳持续未接收" + difference + "分钟");
        }
    }

    private void saveIrrigationLog(FmcBeacon fmcBeacon, String detail) {
        IrrigationLog irrigationLog = new IrrigationLog();
        irrigationLog.setOperationDetail("设备" + fmcBeacon.getDid() + detail);
        irrigationLog.setOperationType("ALARM");
        irrigationLogService.save(irrigationLog);
    }

    @Override
    public boolean checkItExists(String unitCode) {
        long count = this.count(new QueryWrapper<IrrigationUnit>().eq("unit_code", unitCode));
        return count <= 0;
    }

    @Override
    public void updateWaterVolumeInfo(IrrigationUnitVO vo) {
        UpdateWrapper<IrrigationUnit> wrapper = new UpdateWrapper<>();
        wrapper.eq("unit_code", vo.getUnitCode());
        if (ObjectUtil.isNotNull(vo.getSoilMoisture())) {
            wrapper.set("soil_moisture", vo.getSoilMoisture());
        }
        if (ObjectUtil.isNotNull(vo.getFlowRemaining())) {
            wrapper.set("flow_remaining", vo.getFlowRemaining());
        }
        if (ObjectUtil.isNotNull(vo.getTimeRemaining())) {
            wrapper.set("time_remaining", vo.getTimeRemaining());
        }
        if (ObjectUtil.isNotNull(vo.getEvapotranspiration())) {
            wrapper.set("evapotranspiration", vo.getEvapotranspiration());
        }
        this.update(wrapper);
    }

    @Override
    public IrrigationUnitVO getIrrigationUnitById(Long id) {
        IrrigationUnitVO vo = IrrigationUnitConvert.INSTANCE.convert(this.getById(id));
        if (ObjectUtil.isNotNull(vo)) {
            vo.setRationTypeMsg((JSONObject) JSONObject.toJSON(rationService.getOne(new LambdaQueryWrapper<Ration>().eq(Ration::getIrrigationUnitUuid, vo.getUuid()))));
            vo.setCycleTypeMsg((JSONObject) JSONObject.toJSON(cycleService.getOne(new LambdaQueryWrapper<Cycle>().eq(Cycle::getIrrigationUnitUuid, vo.getUuid()))));
            MoistureControl one = moistureControlService.getOne(new LambdaQueryWrapper<MoistureControl>().eq(MoistureControl::getIrrigationUnitUuid, vo.getUuid()));
            vo.setMoistureControlTypeMsg((JSONObject) JSONObject.toJSON(moistureControlService.getOne(new LambdaQueryWrapper<MoistureControl>().eq(MoistureControl::getIrrigationUnitUuid, vo.getUuid()))));
            vo.setETTypeMsg((JSONObject) JSONObject.toJSON(E_TService.getOne(new LambdaQueryWrapper<ET>().eq(ET::getIrrigationUnitUuid, vo.getUuid()))));
            FmcMetrics fmcMetrics = fmcMetricsService.getOne(new QueryWrapper<FmcMetrics>().eq("did", vo.getControlDevice()).orderByDesc("id").last("LIMIT 1"));
            if (null != fmcMetrics) {
                vo.setFlowRemaining(fmcMetrics.getFlowRem());
                vo.setEvapotranspiration(fmcMetrics.getEt());
            }
        }
        return vo;
    }

    @Override
    public void decisionCallbacks(IrrigationUnitVO vo) {
       /* String callBackStatus = "1";
        UpdateWrapper<IrrigationUnit> wrapper = new UpdateWrapper<>();
        wrapper.eq("unit_code", vo.getUnitCode());
        if (ObjectUtil.isNotNull(vo.getCallBackStatus()) && "2".equals(vo.getCallBackStatus())) {
            callBackStatus = "2";
            wrapper.set("status", 0);
        }
        wrapper.set("call_back_status", callBackStatus);
        this.update(wrapper);*/
    }

    @Override
    public void setETCheck(String controlDevice) {
        FmcMetrics metrics = fmcMetricsService.getOne(new QueryWrapper<FmcMetrics>().eq("did", controlDevice).orderByDesc("id").last("LIMIT 1"));
        if (null == metrics) {
            FmcMetrics fmcMetrics = new FmcMetrics();
            fmcMetrics.setDid(controlDevice);
            fmcMetrics.setEt(0f);
            fmcMetrics.setIrrigationAmount(0f);
            fmcMetrics.setInitialValue(0f);
            fmcMetrics.setFlowRem(0f);
            fmcMetrics.setSoilMoisture(0f);
            fmcMetricsService.save(fmcMetrics);
            //创建定时任务，每15分钟执行一次，累计ET值
            // cumulativeETHandler(controlDevice);
        }
    }

    @Override
    public void setCycleCheck(String controlDevice, String cycle) {
        cycleService.setCycleCheck(controlDevice, cycle);

    }

    @Override
    public void delTaskByJobId(String controlDevice) {
        FmcMetrics fmcMetrics = fmcMetricsService.getOne(new QueryWrapper<FmcMetrics>().eq("did", controlDevice));
        if (null != fmcMetrics) {
            String adminAddresses = xxlJobConfig.getAdminAddresses();
            XxlJobUtil xxlJobUtil = new XxlJobUtil();
            //删除之前的定时任务
            String jobDesc = fmcMetrics.getJobDesc();
            if (null != jobDesc) {
                String[] descArr = jobDesc.split(",");
                for (String desc : descArr) {
                    xxlJobUtil.removeByCondition(adminAddresses, desc);
                }
            }
        }
    }


    @Override
    public void delete(Long id) {
        removeById(id);
    }

    @Override
    public void upCycleCron(){
        String adminAddresses = xxlJobConfig.getAdminAddresses();
        XxlJobUtil xxlJobUtil = new XxlJobUtil();
        //所有周期灌溉单元
        List<IrrigationUnit> irrigationUnitList = this.list(new QueryWrapper<IrrigationUnit>().eq("type", "02"));
        for (IrrigationUnit irrigationUnit : irrigationUnitList) {
            FmcMetrics fmcMetrics = fmcMetricsService.getOne(new QueryWrapper<FmcMetrics>().eq("did", irrigationUnit.getControlDevice()));
            if(null != fmcMetrics){
                log.info("xxl-job地址：{}",adminAddresses);
                xxlJobUtil.removeByCondition(adminAddresses, fmcMetrics.getJobDesc());
                Cycle cycle = cycleService.getOne(new QueryWrapper<Cycle>().select("irrigation_date").eq("irrigation_unit_uuid", irrigationUnit.getUuid()));
                this.setCycleCheck(irrigationUnit.getControlDevice(),cycle.getIrrigationDate());
            }

        }
    }

}